﻿using System;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using ReactiveCode.Util;
using System.Collections.Generic;

namespace ReactiveCode.Linq
{
    using Observables;

    abstract class UnaryOperator<TSource, TResult>: LinqOperator<TResult>, IEnumerableObserver<TSource>
    {
        protected class Entry: IDisposable
        {
            public Action Remove;
            public IDisposable Subscription;

            public void Dispose()
            {
                if (Subscription != null)
                    Subscription.Dispose();
            }
        }
        Dictionary<IObservable<TSource>, Entry> _entries = new Dictionary<IObservable<TSource>, Entry>();

        public UnaryOperator(IEnumerableObserver<TResult> observer)
            :base(observer)
        {
        }
        public override void Dispose()
        {
            foreach (var entry in _entries.Values)
                entry.Dispose();
        }

        protected abstract Entry OnAdded(IObservable<TSource> item);
        public virtual void OnCleared()
        {
            foreach (var entry in _entries.Values)
                entry.Dispose();
            _entries.Clear();
            Observer.OnCleared();
        }

        public void OnAdded(IObservable<TSource> item, int index)
        {
            if (_entries.ContainsKey(item))
                item = item.Clone();
            _entries[item] = OnAdded(item);
        }
        public void OnRemoved(IObservable<TSource> item, int index)
        {
            Entry entry;
            if (_entries.TryGetValue(item, out entry))
            {
                entry.Remove();
                entry.Dispose();
            }
        }
        public void OnReset(IEnumerable<IObservable<TSource>> items)
        {
            OnCleared();
            foreach (var item in items)
                OnAdded(item, -1);
        }

        public virtual void OnError(Exception error)
        {
            Observer.OnError(error);
        }
        public virtual void OnCompleted()
        {
            Observer.OnCompleted();
        }
    }
}
